use crate::common::{Digital, TwoInputDigital};
use crate::output::State::{ONE, ZERO};
use crate::output::{DigitalState, State};
use std::rc::Rc;
use crate::Digital1Line;

#[allow(unused)]
///OrElectric structure represent the OR logic circuit
/// ## EXAMPLE
///```rust
/// //create sig1
/// use rust_play_digital::{Digital, Digital1Line, InitSig, OrElectric};
/// use rust_play_digital::State::{ONE, ZERO};
///
/// let  init_sig1 = InitSig::new(vec![Some(ZERO),Some(ONE),Some(ZERO)]);
/// let  init_sig2 = InitSig::new(vec![Some(ONE),Some(ONE),Some(ZERO)]);
///    //deep clone and link sig to Or logic circuit input a
///    //deep clone and link sig to Or logic circuit input b
/// let  or_electric = OrElectric::process(Some(init_sig1),Some(init_sig2));
///    //get Or logic circuit output
/// println!("{:?}", or_electric.get_digital_output().output_line_x(0));
///
/// ```

pub struct OrElectric {
    input_a: Option<Rc<dyn Digital1Line>>,
    input_b: Option<Rc<dyn Digital1Line>>,
    input_a_states: DigitalState,
    input_b_states: DigitalState,
    output_states: DigitalState,
    times: usize,
}
#[allow(unused)]
impl Digital1Line for OrElectric{}
#[allow(unused)]
impl Digital for OrElectric {
    fn get_output(&self) -> DigitalState {
        self.output_states.clone()
    }
}

impl OrElectric {
    ///This function stands for handling a Or gate logic operation
    pub fn process(input_a: Option<Rc<dyn Digital1Line>>, input_b: Option<Rc<dyn Digital1Line>>) -> Rc<Self> {
        match (input_a, input_b) {
            (Some(a), Some(b)) => {
                let input_a_states = a.get_digital_output();
                let input_b_states = b.get_digital_output();
                let output_state = input_a_states
                    .output_line_x(0)
                    .clone()
                    .into_iter()
                    .zip(input_b_states.output_line_x(0).into_iter())
                    .map(|(a_state, b_state)| Self::process_double_input(a_state, b_state))
                    .collect::<Vec<Option<State>>>();
                let output_state = Self::handle_loop_circuit(output_state);
                let output_states = DigitalState::new(vec![output_state.clone()], 1, output_state.len());
                let times = output_states.per_line_signals_count();
                Rc::new(OrElectric::new(
                    Some(a.clone()),
                    Some(b.clone()),
                    input_a_states,
                    input_b_states,
                    output_states,
                    times,
                ))
            }
            (Some(a), None) => {
                let input_b = None;
                let input_b_state = DigitalState::new(vec![vec![]], 0, 0);
                let input_a = Some(a.clone());
                let input_a_state = a.get_digital_output();
                let output_state = Self::process_single_input(input_a_state.clone());
                let times = output_state.per_line_signals_count();
                Rc::new(OrElectric::new(
                    input_a,
                    input_b,
                    input_a_state,
                    input_b_state,
                    output_state,
                    times,
                ))
            }
            (None, Some(b)) => {
                let input_a = None;
                let input_a_state = DigitalState::new(vec![vec![]], 1, 0);
                let input_b = Some(b.clone());
                let input_b_state = b.get_digital_output();
                let output_state = Self::process_single_input(input_b_state.clone());
                let times = output_state.per_line_signals_count();
                Rc::new(OrElectric::new(
                    input_a,
                    input_b,
                    input_a_state,
                    input_b_state,
                    output_state,
                    times,
                ))
            }
            (None, None) => {
                let output_state = DigitalState::new(vec![vec![]], 1, 0);
                let input_a = None;
                let input_a_state = DigitalState::new(vec![vec![]], 1, 0);
                let input_b = None;
                let input_b_state = DigitalState::new(vec![vec![]], 1, 0);
                Rc::new(OrElectric::new(
                    input_a,
                    input_b,
                    input_a_state,
                    input_b_state,
                    output_state,
                    0,
                ))
            }
        }
    }

    ///The purpose of this function is to deal with the situation of the loop circuit such as the stable output of the latch circuit.
    /// <div class="warning">
    ///
    ///crate will not give correct simulation results when simulating unstable circuits.
    ///Make sure you do not simulate unstable circuits.
    ///
    /// </div>
    fn handle_loop_circuit(mut output_state: Vec<Option<State>>) -> Vec<Option<State>> {
        let times = output_state.len();
        if output_state.len() > 1 {
            (1..times).into_iter().for_each(|t| {
                if output_state[t].clone().is_none() {
                    output_state[t] = output_state[t - 1].clone()
                }
            });
        };
        output_state
    }

    fn process_double_input(a_state: Option<State>, b_state: Option<State>) -> Option<State> {
        if let (Some(a), Some(b)) = (a_state.clone(), b_state.clone()) {
            if a == ONE || b == ONE {
                Some(ONE)
            } else {
                Some(ZERO)
            }
        } else if let (Some(a), None) = (a_state.clone(), b_state.clone()) {
            if a == ONE { Some(ONE) } else { None }
        } else if let (None, Some(b)) = (a_state.clone(), b_state.clone()) {
            if b == ONE { Some(ONE) } else { None }
        } else {
            None
        }
    }
    fn process_single_input(input_state: DigitalState) -> DigitalState {
        let s = input_state
            .output_line_x(0)
            .into_iter()
            .map(|a_state| {
                if let Some(a) = a_state {
                    if a == ONE { Some(ONE) } else { None }
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();
        DigitalState::new(vec![s.clone()], 1, s.len())
    }
}

impl OrElectric {
    pub fn new(
        input_a: Option<Rc<dyn Digital1Line>>,
        input_b: Option<Rc<dyn Digital1Line>>,
        input_a_states: DigitalState,
        input_b_states: DigitalState,
        output_states: DigitalState,
        times: usize,
    ) -> Self {
        Self {
            input_a,
            input_b,
            input_a_states,
            input_b_states,
            output_states,
            times,
        }
    }

    pub fn input_a(&self) -> Option<Rc<dyn Digital1Line>> {
        self.input_a.clone()
    }

    pub fn input_b(&self) -> Option<Rc<dyn Digital1Line>> {
        self.input_b.clone()
    }

    pub fn input_a_states(&self) -> DigitalState {
        self.input_a_states.clone()
    }

    pub fn input_b_states(&self) -> DigitalState {
        self.input_b_states.clone()
    }

    pub fn output_states(&self) -> DigitalState {
        self.output_states.clone()
    }

    pub fn times(&self) -> usize {
        self.times
    }
}
#[allow(unused)]
impl TwoInputDigital for OrElectric {
    fn set_input_a(&self, input_a: Option<Rc<dyn Digital1Line>>) -> Rc<Self> {
        Self::process(input_a, self.input_b.clone())
    }

    fn set_input_b(&self, input_b: Option<Rc<dyn Digital1Line>>) -> Rc<Self> {
        Self::process(self.input_a.clone(), input_b)
    }
}
